﻿using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.Experimental.GraphView;
using System.Linq;
using System.Text;

namespace NodeEditor
{
    public class SearchWindowProvider : ScriptableObject, ISearchWindowProvider
    {
        class Entry
        {
            public string path;
            public SearchTreeEntry entry;
        }

        readonly List<SearchTreeEntry> sources = new List<SearchTreeEntry>();
        readonly List<SearchTreeEntry> result = new List<SearchTreeEntry>();

        public EditorWindow editorWindow;
        public NodeView nodeView;

        public static SearchWindowProvider Create(EditorWindow editorWindow, NodeView nodeView)
        {
            var provider = ScriptableObject.CreateInstance<SearchWindowProvider>();

            provider.editorWindow = editorWindow;
            provider.nodeView = nodeView;

            nodeView.nodeCreationRequest += (NodeCreationContext context) => {
                SearchWindow.Open(new SearchWindowContext(context.screenMousePosition), provider);
            };

            var nodeAttribute = typeof(NodeAttribute);
            var types = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(a => a.GetTypes())
                .Where(
                //不是抽象类型
                t => !t.IsAbstract
                // 继承 AbstractNode
                && t.IsSubclassOf(typeof(AbstractNode))
                //拥有 nodeAttribute 特性
                && t.GetCustomAttributes(nodeAttribute, false).Length!=0)
                .ToArray();

            StringBuilder sb = new StringBuilder();
            HashSet<string> dict = new HashSet<string>();
            List<Entry> list = new List<Entry>();
            foreach (var type in types)
            {
                var attr = type.GetCustomAttributes(nodeAttribute, false);
                
                var nodeAttr= attr[0] as NodeAttribute;
                for (int i=0,end= nodeAttr.titles.Length-1;i<end;i++)
                {
                    sb.Append(nodeAttr.titles[i]);
                    if(!dict.Contains(sb.ToString()))
                    {
                        dict.Add(sb.ToString());
                        list.Add(CreateEntry(sb.ToString(), i + 1, null, () => new SearchTreeGroupEntry(new GUIContent(nodeAttr.titles[i]))));
                    }
                }

                var name = nodeAttr.titles[nodeAttr.titles.Length - 1];
                sb.Append(name);
                list.Add(CreateEntry(sb.ToString(), nodeAttr.titles.Length, type,()=> new SearchTreeEntry(new GUIContent(name))));
                sb.Length = 0;
            }
            list.Sort((a, b) => a.path.CompareTo(b.path));
            foreach (var item in list)
            {
                provider.sources.Add(item.entry);
            }
            return provider;
        }
        
        static Entry CreateEntry(string path,int level,object data,Func<SearchTreeEntry> func)
        {
            var entry = func();
            entry.level = level;
            entry.userData = data;
            return new Entry
            {
                path = path,
                entry = entry
            };
        }

        List<SearchTreeEntry> ISearchWindowProvider.CreateSearchTree(SearchWindowContext context)
        {
            result.Clear();

            result.Add(new SearchTreeGroupEntry(new GUIContent("CreateNode")));


            foreach (var item in sources)
            {
                result.Add(item);
            }
            return result;
        }

        bool ISearchWindowProvider.OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
        {
            var type = (Type)SearchTreeEntry.userData;
            var nodeItem = nodeView.CreateNode(type);

            var windowPos = editorWindow.rootVisualElement.ChangeCoordinatesTo(
                editorWindow.rootVisualElement.parent,
                context.screenMousePosition-editorWindow.position.position
                );

            var viewPos = nodeView.contentViewContainer.WorldToLocal(windowPos);

            nodeItem.SetPosition(new Rect(viewPos, Vector2.zero));
            return true;
        }

      

    }


    
}